﻿/*!
* mustache.js - Logic-less {{mustache}} templates with JavaScript
* http://github.com/janl/mustache.js
*/
var Mustache = (typeof module !== "undefined" && module.exports) || {};

(function (exports)
{

	exports.name = "mustache.js";
	exports.version = "0.5.0-dev";
	exports.tags = ["{{", "}}"];
	exports.parse = parse;
	exports.compile = compile;
	exports.render = render;
	exports.clearCache = clearCache;
	exports.getCache = getCache;

	// This is here for backwards compatibility with 0.4.x.
	exports.to_html = function (template, view, partials, send)
	{
		var result = render(template, view, partials);

		if (typeof send === "function")
		{
			send(result);
		} else
		{
			return result;
		}
	};

	var _toString = Object.prototype.toString;
	var _isArray = Array.isArray;
	var _forEach = Array.prototype.forEach;
	var _trim = String.prototype.trim;

	var isArray;
	if (_isArray)
	{
		isArray = _isArray;
	} else
	{
		isArray = function (obj)
		{
			return _toString.call(obj) === "[object Array]";
		};
	}

	var forEach;
	if (_forEach)
	{
		forEach = function (obj, callback, scope)
		{
			return _forEach.call(obj, callback, scope);
		};
	} else
	{
		forEach = function (obj, callback, scope)
		{
			for (var i = 0, len = obj.length; i < len; ++i)
			{
				callback.call(scope, obj[i], i, obj);
			}
		};
	}

	var spaceRe = /^\s*$/;

	function isWhitespace(string)
	{
		return spaceRe.test(string);
	}

	var trim;
	if (_trim)
	{
		trim = function (string)
		{
			return string == null ? "" : _trim.call(string);
		};
	} else
	{
		var trimLeft, trimRight;

		if (isWhitespace("\xA0"))
		{
			trimLeft = /^\s+/;
			trimRight = /\s+$/;
		} else
		{
			// IE doesn't match non-breaking spaces with \s, thanks jQuery.
			trimLeft = /^[\s\xA0]+/;
			trimRight = /[\s\xA0]+$/;
		}

		trim = function (string)
		{
			return string == null ? "" :
        String(string).replace(trimLeft, "").replace(trimRight, "");
		};
	}

	var escapeMap = {
		"&": "&amp;",
		"<": "&lt;",
		">": "&gt;",
		'"': '&quot;',
		"'": '&#39;'
	};

	function escapeHTML(string)
	{
		return String(string).replace(/&(?!\w+;)|[<>"']/g, function (s)
		{
			return escapeMap[s] || s;
		});
	}

	/**
	* Adds the `template`, `line`, and `file` properties to the given error
	* object and alters the message to provide more useful debugging information.
	*/
	function debug(e, template, line, file)
	{
		file = file || "<template>";

		var lines = template.split("\n"),
        start = Math.max(line - 3, 0),
        end = Math.min(lines.length, line + 3),
        context = lines.slice(start, end);

		var c;
		for (var i = 0, len = context.length; i < len; ++i)
		{
			c = i + start + 1;
			context[i] = (c === line ? " >> " : "    ") + context[i];
		}

		e.template = template;
		e.line = line;
		e.file = file;
		e.message = [file + ":" + line, context.join("\n"), "", e.message].join("\n");

		return e;
	}

	/**
	* Looks up the value of the given `name` in the given context `stack`.
	*/
	function lookup(name, stack, defaultValue)
	{
		if (name === ".")
		{
			return stack[stack.length - 1];
		}

		var names = name.split(".");
		var lastIndex = names.length - 1;
		var target = names[lastIndex];

		var value, context, i = stack.length, j, localStack;
		while (i)
		{
			localStack = stack.slice(0);
			context = stack[--i];

			j = 0;
			while (j < lastIndex)
			{
				context = context[names[j++]];

				if (context == null)
				{
					break;
				}

				localStack.push(context);
			}

			if (context && target in context)
			{
				value = context[target];
				break;
			}
		}

		// If the value is a function, call it in the current context.
		if (typeof value === "function")
		{
			value = value.call(localStack[localStack.length - 1]);
		}

		if (value == null)
		{
			return defaultValue;
		}

		return value;
	}

	function renderSection(name, stack, callback, inverted)
	{
		var buffer = "";
		var value = lookup(name, stack);

		if (inverted)
		{
			// From the spec: inverted sections may render text once based on the
			// inverse value of the key. That is, they will be rendered if the key
			// doesn't exist, is false, or is an empty list.
			if (value == null || value === false || (isArray(value) && value.length === 0))
			{
				buffer += callback();
			}
		} else if (isArray(value))
		{
			forEach(value, function (value)
			{
				stack.push(value);
				buffer += callback();
				stack.pop();
			});
		} else if (typeof value === "object")
		{
			stack.push(value);
			buffer += callback();
			stack.pop();
		} else if (typeof value === "function")
		{
			var scope = stack[stack.length - 1];
			var scopedRender = function (template)
			{
				return render(template, scope);
			};
			buffer += value.call(scope, callback(), scopedRender) || "";
		} else if (value)
		{
			buffer += callback();
		}

		return buffer;
	}

	/**
	* Parses the given `template` and returns the source of a function that,
	* with the proper arguments, will render the template. Recognized options
	* include the following:
	*
	*   - file     The name of the file the template comes from (displayed in
	*              error messages)
	*   - tags     An array of open and close tags the `template` uses. Defaults
	*              to the value of Mustache.tags
	*   - debug    Set `true` to log the body of the generated function to the
	*              console
	*   - space    Set `true` to preserve whitespace from lines that otherwise
	*              contain only a {{tag}}. Defaults to `false`
	*/
	function parse(template, options)
	{
		options = options || {};

		var tags = options.tags || exports.tags,
        openTag = tags[0],
        closeTag = tags[tags.length - 1];

		var code = [
      'var buffer = "";', // output buffer
      "\nvar line = 1;", // keep track of source line number
      "\ntry {",
      '\nbuffer += "'
    ];

		var spaces = [],      // indices of whitespace in code on the current line
        hasTag = false,   // is there a {{tag}} on the current line?
        nonSpace = false; // is there a non-space char on the current line?

		// Strips all space characters from the code array for the current line
		// if there was a {{tag}} on it and otherwise only spaces.
		var stripSpace = function ()
		{
			if (hasTag && !nonSpace && !options.space)
			{
				while (spaces.length)
				{
					code.splice(spaces.pop(), 1);
				}
			} else
			{
				spaces = [];
			}

			hasTag = false;
			nonSpace = false;
		};

		var sectionStack = [], updateLine, nextOpenTag, nextCloseTag;

		var setTags = function (source)
		{
			tags = trim(source).split(/\s+/);
			nextOpenTag = tags[0];
			nextCloseTag = tags[tags.length - 1];
		};

		var includePartial = function (source)
		{
			code.push(
        '";',
        updateLine,
        '\nvar partial = partials["' + trim(source) + '"];',
        '\nif (partial) {',
        '\n  buffer += render(partial,stack[stack.length - 1],partials);',
        '\n}',
        '\nbuffer += "'
      );
		};

		var openSection = function (source, inverted)
		{
			var name = trim(source);

			if (name === "")
			{
				throw debug(new Error("Section name may not be empty"), template, line, options.file);
			}

			sectionStack.push({ name: name, inverted: inverted });

			code.push(
        '";',
        updateLine,
        '\nvar name = "' + name + '";',
        '\nvar callback = (function () {',
        '\n  return function () {',
        '\n    var buffer = "";',
        '\nbuffer += "'
      );
		};

		var openInvertedSection = function (source)
		{
			openSection(source, true);
		};

		var closeSection = function (source)
		{
			var name = trim(source);
			var openName = sectionStack.length != 0 && sectionStack[sectionStack.length - 1].name;

			if (!openName || name != openName)
			{
				throw debug(new Error('Section named "' + name + '" was never opened'), template, line, options.file);
			}

			var section = sectionStack.pop();

			code.push(
        '";',
        '\n    return buffer;',
        '\n  };',
        '\n})();'
      );

			if (section.inverted)
			{
				code.push("\nbuffer += renderSection(name,stack,callback,true);");
			} else
			{
				code.push("\nbuffer += renderSection(name,stack,callback);");
			}

			code.push('\nbuffer += "');
		};

		var sendPlain = function (source)
		{
			code.push(
        '";',
        updateLine,
        '\nbuffer += lookup("' + trim(source) + '",stack,"");',
        '\nbuffer += "'
      );
		};

		var sendEscaped = function (source)
		{
			code.push(
        '";',
        updateLine,
        '\nbuffer += escapeHTML(lookup("' + trim(source) + '",stack,""));',
        '\nbuffer += "'
      );
		};

		var line = 1, c, callback;
		for (var i = 0, len = template.length; i < len; ++i)
		{
			if (template.slice(i, i + openTag.length) === openTag)
			{
				i += openTag.length;
				c = template.substr(i, 1);
				updateLine = '\nline = ' + line + ';';
				nextOpenTag = openTag;
				nextCloseTag = closeTag;
				hasTag = true;

				switch (c)
				{
					case "!": // comment
						i++;
						callback = null;
						break;
					case "=": // change open/close tags, e.g. {{=<% %>=}}
						i++;
						closeTag = "=" + closeTag;
						callback = setTags;
						break;
					case ">": // include partial
						i++;
						callback = includePartial;
						break;
					case "#": // start section
						i++;
						callback = openSection;
						break;
					case "^": // start inverted section
						i++;
						callback = openInvertedSection;
						break;
					case "/": // end section
						i++;
						callback = closeSection;
						break;
					case "{": // plain variable
						closeTag = "}" + closeTag;
						// fall through
					case "&": // plain variable
						i++;
						nonSpace = true;
						callback = sendPlain;
						break;
					default: // escaped variable
						nonSpace = true;
						callback = sendEscaped;
				}

				var end = template.indexOf(closeTag, i);

				if (end === -1)
				{
					throw debug(new Error('Tag "' + openTag + '" was not closed properly'), template, line, options.file);
				}

				var source = template.substring(i, end);

				if (callback)
				{
					callback(source);
				}

				// Maintain line count for \n in source.
				var n = 0;
				while (~(n = source.indexOf("\n", n)))
				{
					line++;
					n++;
				}

				i = end + closeTag.length - 1;
				openTag = nextOpenTag;
				closeTag = nextCloseTag;
			} else
			{
				c = template.substr(i, 1);

				switch (c)
				{
					case '"':
					case "\\":
						nonSpace = true;
						code.push("\\" + c);
						break;
					case "\r":
						// Ignore carriage returns.
						break;
					case "\n":
						spaces.push(code.length);
						code.push("\\n");
						stripSpace(); // Check for whitespace on the current line.
						line++;
						break;
					default:
						if (isWhitespace(c))
						{
							spaces.push(code.length);
						} else
						{
							nonSpace = true;
						}

						code.push(c);
				}
			}
		}

		if (sectionStack.length != 0)
		{
			throw debug(new Error('Section "' + sectionStack[sectionStack.length - 1].name + '" was not closed properly'), template, line, options.file);
		}

		// Clean up any whitespace from a closing {{tag}} that was at the end
		// of the template without a trailing \n.
		stripSpace();

		code.push(
      '";',
      "\nreturn buffer;",
      "\n} catch (e) { throw {error: e, line: line}; }"
    );

		// Ignore `buffer += "";` statements.
		var body = code.join("").replace(/buffer \+= "";\n/g, "");

		if (options.debug)
		{
			if (typeof console != "undefined" && console.log)
			{
				console.log(body);
			} else if (typeof print === "function")
			{
				print(body);
			}
		}

		return body;
	}

	function getCache()
	{
		return _cache;
	}

	/**
	* Used by `compile` to generate a reusable function for the given `template`.
	*/
	function _compile(template, options)
	{
		var args = "view,partials,stack,lookup,escapeHTML,renderSection,render";
		var body = parse(template, options);
		var fn = new Function(args, body);

		// This anonymous function wraps the generated function so we can do
		// argument coercion, setup some variables, and handle any errors
		// encountered while executing it.
		return function (view, partials)
		{
			partials = partials || {};

			var stack = [view]; // context stack

			try
			{
				return fn(view, partials, stack, lookup, escapeHTML, renderSection, render);
			} catch (e)
			{
				throw debug(e.error, template, e.line, options.file);
			}
		};
	}

	// Cache of pre-compiled templates.
	var _cache = {};

	/**
	* Clear the cache of compiled templates.
	*/
	function clearCache()
	{
		_cache = {};
	}

	/**
	* Compiles the given `template` into a reusable function using the given
	* `options`. In addition to the options accepted by Mustache.parse,
	* recognized options include the following:
	*
	*   - cache    Set `false` to bypass any pre-compiled version of the given
	*              template. Otherwise, a given `template` string will be cached
	*              the first time it is parsed
	*/
	function compile(template, options)
	{
		options = options || {};

		// Use a pre-compiled version from the cache if we have one.
		if (options.cache !== false)
		{
			if (!_cache[template])
			{
				_cache[template] = _compile(template, options);
			}

			return _cache[template];
		}

		return _compile(template, options);
	}

	/**
	* High-level function that renders the given `template` using the given
	* `view` and `partials`. If you need to use any of the template options (see
	* `compile` above), you must compile in a separate step, and then call that
	* compiled function.
	*/
	function render(template, view, partials)
	{
		return compile(template)(view, partials);
	}

})(Mustache);
